設計流程的出現,讓我們可以寫出一套好的流程,並且幫助團隊少寫多餘的程式碼。由於Ruby不像Javascript,是標準的物件導向語言,當然也可以使用各種形式的設計流程。今天,我們會介紹常用、漂亮的設計流程。
我們先舉以下為寄發簡訊的 Message
服務為例
class Sms::SendMessage
def self.call(phone, code: nil)
new(phone, code).call
end
def initialize(phone, code)
# @host, @username, @password 為存在專案內部的加密變數
@host = Rails.application.credentials.sms[:host]
@username = Rails.application.credentials.sms[:username]
@password = Rails.application.credentials.sms[:password]
# @dstaddr: 電話號碼
# @smbody: 簡訊欸榮
@dstaddr = phone
@smbody = "#{code} is your verification code."
end
attr_reader :host, :username, :password, :dstaddr, :smbody
def call
ApiClient.post host, headers: nil, payload: { username: username, password: password, dstaddr: dstaddr, smbody: smbody }
end
end
其中ApiClient
在Day12提到過,是專門打API用的類別方法。
Sms::SendMessage
使用方式如下,第一個參數為欲寄發簡訊的電話號碼,第二個參數為驗證碼。
Sms::SendMessage.call('0983168969', code: '123456')
只要填妥兩個參數,就可以發簡訊了!
我們習慣使用call
表示我們要呼叫該參數,而call
裡面包含呼叫實體變數,而這種行為我們稱為委託delegate
。將self.new(*params).call
的行為委託給call
去執行的設計流程,稱為Service Pattern
def self.call(phone, code: nil)
# self.new(phone, code).call 的簡寫
new(phone, code).call
end
像這種單一class
負責單一職責的設計模式Service Pattern
, 是Rails常見的設計模式之一。
自己曾做過的電商專案中,除了簡訊發送Service以外,還有以下這些 Service。
第三方相關
金流相關
綠界付款
綠界退款
綠界開立發票
綠界折讓發票
綠界查詢付款狀態
物流相關
順豐物流下單
順豐物流查詢貨態
簡訊相關
發送驗證碼
電商相關
付款動作
退款動作
POS給點動作
這些Service的目的只為單一職責(一次只做一件事)。舉例來說因此若排程端、後台使用者端、顧客端需要做退款動作的時候,只需要呼叫退款流程 service
即可。而這些Service
與MVC
核心架構耦合性越低,重複使用的可能性跟便利性就更高,在同個專案所使用的Service 多和 ActiveRecord
耦合,若要成為給大家復用的Gem
,那就必須寫出耦合性更低的程式碼。
舉簡單的例子講解Service Pattern
class AppleService
def self.call(*args, &block)
new(*args).call(&block)
end
def initialize(a, b = nil, options = {})
@a, @b,@c = a, b, options[:c]
end
attr_reader :a, :b, :c
def call
p "=== what is a: #{a} ==="
p "=== what is b: #{b} ==="
p "=== what is c: #{c} ==="
if block_given?
yield(a, b, c)
else
{a: a, b: b, c: c}
end
end
end
至於如何使用參數,我們在Day11提及過,這裡就先簡單舉例AppleService
帶入不同參數的結果。
AppleService.call("a") #=> {:a=>"a", :b=>nil, :c=>nil}
AppleService.call("a", c: 1) #=> {:a=>"a", :b=>{:c=>1}, :c=>nil}
AppleService.call("a", nil, c: 1) #=> {:a=>"a", :b=>nil, :c=>1}
我們也可以搭配 block
使用(Block 的使用可以參考Day9、Day10)
以下區塊負責的內容是將收到的三個值用斜線接起來再回傳,區塊是個有趣的章節,推薦讀者在學Rails
的時候切萬不要退避三舍
AppleService.call("a", "b", c: 1) {|x,y,z| [x,y,z].join('/')} #=> "a/b/1"
當我們對某物件新增單體方法,只有該物件有獨特技能,而若將某類別新增單體方法的話,則為類別方法。Day12、 Day13 提過,這邊就不細談
這是我個人常使用的Pattern
之一,不管是打Api、串接金流、串接物流、匯入表單等變化性很低(偶爾才有變化)的動作,都可以用Strategy Pattern
來去實現!Strategy Pattern
可以用來設定一套流程,我們舉一個不存取值的例子來講:
module Strategy
DEFAULT_STRATEGY = {
params: -> { p '組成資料' },
perform: -> { p '打Api' },
receiver: -> { p '成功或失敗' },
handler: -> { p '取得 response payload 並處理' }
}
def get_sty_flow
block_given? ? yield(DEFAULT_STRATEGY) : DEFAULT_STRATEGY
end
def sty_flow
get_sty_flow.map do |k, v|
# 先找有沒有前綴為sty的方法
go_method = "sty_#{k}".to_sym
# 沒有的話取得 get_sty_flow 的 key
go_method = v if !self.respond_to?(go_method)
next unless go_method.present?
# 判斷是否是Proc,若不是則視為 method
if go_method.is_a? Proc
go_method.call
else
self.send(go_method)
end
# 回傳key(無意義)
k
end
end
end
看到 DEFAULT_STRATEGY
對應 Proc,就有一種寫Javascript
function可以當作變數的感覺。
說明:
1. 流程會寫在 get_sty_flow 方法內,而DEFAULT_STRATEGY 為預設的流程
2. 執行流程的地方在 sty_flow
3. DEFAULT_STRATEGY 的value可以是Proc,也可以是symbol
接著我們介紹以下三種情境。
情境A ➡️ 使用預設的Strategy
class A
include Strategy
end
A.new.sty_flow
#
#======== 印出以下資訊 ========#
#
# "組成資料",
# "打Api",
# "成功或失敗",
# "取得 response payload 並處理"
#
#=> [:params, :perform, :receiver, :handler]
情境B ➡️ 使用繼承,增添客製化的流程processor
class B
include Strategy
def get_sty_flow
super do |strategy|
{ **strategy, processor: -> {'資料處理'} }
end
end
end
B.new.sty_flow
#
#======== 印出以下資訊 ========#
#
# "組成資料",
# "打Api",
# "成功或失敗",
# "取得 response payload 並處理"
#
#=> [:params, :perform, :receiver, :handler, :processor]
情境C ➡️ 使用客製化的方法 sty_params
class C
include Strategy
def sty_params
p '在C組成資料,而非在預設流程'
end
end
C.new.sty_flow
#
#======== 印出以下資訊 ========#
#
# "在C組成資料,而非在預設流程",
# "打Api",
# "成功或失敗",
# "取得 response payload 並處理"
#
#=> [:params, :perform, :receiver, :handler]
我們可以發現上面的方法是真的都可以被抽換的,符合 Strategy Pattern 的精神,也很Gurustrategy
網站所使用的icon
。
其實還有很多設計流程沒有講到,但礙於篇幅的關係,今天先講三個。在Day32講解攤提時,我們會介紹另一個設計流程Decorator Pattern
。
Class 系列到此告一個段落,明天開始講Dynamic Programming